package com.ox.head.okex.service.impl;


import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ox.head.okex.properties.OkProperties;
import com.ox.head.okex.request.FillsRequest;
import com.ox.head.okex.request.OrderRequest;
import com.ox.head.okex.vo.FillsVo;
import com.ox.head.okex.vo.OrderVo;
import lombok.RequiredArgsConstructor;
import com.ox.head.okex.common.OkHeader;
import com.ox.head.okex.common.sign.OkSignUtil;
import com.ox.head.okex.common.time.DateUtils;
import com.ox.head.okex.common.url.OkSpotCommons;
import com.ox.head.okex.service.OkSpotApiClient;
import com.ox.head.okex.vo.Accounts;
import lombok.SneakyThrows;
import org.springframework.boot.configurationprocessor.json.JSONStringer;
import org.springframework.http.HttpHeaders;
import org.springframework.lang.NonNull;

import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;


import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

/**
 * @author even
 * @date 2021/5/14 0014 上午 11:50
 * @describe
 */
@RequiredArgsConstructor
public class OkSpotApiClientImpl implements OkSpotApiClient {

    private final WebClient webClient;
    private final OkProperties okProperties;








    /**
     * 添加头信息
     * @param timestamp
     * @param sign
     * @return
     * @ahtor even
     * @since
     */
    private Consumer<HttpHeaders> addHeader(String timestamp, String sign){
        Consumer<HttpHeaders>   headersConsumer = httpHeaders -> {
            httpHeaders.add(OkHeader.HEADER_ACCESS_TIMESTAMP, timestamp);
            httpHeaders.add(OkHeader.HEADER_ACCESS_SIGN,sign);
        };
        return headersConsumer;
    }


    /**
     * 不带参数的集合
     * @param url
     * @param tClass
     * @return
     * @ahtor even
     * @since
     */
    private <T> Flux<T> getWebClient(String url, Class<T> tClass){
        final String timestamp= DateUtils.timestamp();
        return webClient.get()
                .uri(url).headers(this.addHeader(timestamp, OkSignUtil.getSign(timestamp,url,null,okProperties.getAccessSecret())))
                .retrieve().bodyToFlux(tClass);
    }
    /**
     * 获取MONO
     * @param url
     * @param tClass
     * @return
     * @ahtor even
     * @since
     */
    private <T> Mono<T> getMonoWebClient(String url, Class<T> tClass){
        final String timestamp=DateUtils.timestamp();
        final String sign = OkSignUtil.getSign(timestamp, url, null, okProperties.getAccessSecret());
        return webClient.get()
                .uri(url)
                .headers(this.addHeader(timestamp,sign))
                .retrieve().bodyToMono(tClass);
    }

    /**
     * post 请求
     * @param url
     * @param body
     * @param tClass
     * @return
     * @ahtor even
     * @since
     */
    private <T> Mono<T> postWebClient(String url,String body,Class<T> tClass){
        final String timestamp=DateUtils.timestamp();
        final String sign = OkSignUtil.postSign(timestamp, url, body, okProperties.getAccessSecret());
        return webClient
                .post()
                .uri(url)
                .bodyValue(body)
                .headers(this.addHeader(timestamp,sign))
                .retrieve().bodyToMono(tClass);
    }

    /**
     * Flux
     * @param url
     * @param body
     * @param tClass
     * @return
     * @ahtor even
     * @since
     */
    private <T> Flux<T> postFluxWebClient(String url,String body,Class<T> tClass){
        final String timestamp=DateUtils.timestamp();
        final String sign = OkSignUtil.postSign(timestamp, url, body, okProperties.getAccessSecret());
        return webClient
                .post()
                .uri(url)
                .bodyValue(body)
                .headers(this.addHeader(timestamp,sign))
                .retrieve().bodyToFlux(tClass);
    }


    @Override
    public Flux<Accounts> getAllWallet() {
        return this.getWebClient(OkSpotCommons.ACCOUNTS,Accounts.class);
    }

    @Override
    public Mono<Accounts> getAccounts(@NonNull String currency) {

        final String path = UriComponentsBuilder.fromUriString(OkSpotCommons.ACCOUNTS_COIN).build(currency).getPath();

        return this.getMonoWebClient(path,Accounts.class);
    }

    @Override
    public Flux<String> getLedger(String currency) {
        final String path=UriComponentsBuilder.fromUriString(OkSpotCommons.ACCOUNTS_LEDGER).build(currency).getPath();
        return this.getWebClient(path,String.class);
    }

    @SneakyThrows
    @Override
    public Mono<OrderVo> postOrder(OrderRequest orderRequest)  {
        ObjectMapper mapper = new ObjectMapper();
        final String json=mapper.writeValueAsString(orderRequest);
        return this.postWebClient(OkSpotCommons.ORDERS,json,OrderVo.class);
    }

    @Override
    @SneakyThrows
    public Flux<OrderVo> batchOrder(List<OrderRequest> orderRequestList) {
        ObjectMapper mapper = new ObjectMapper();
        final String json=mapper.writeValueAsString(orderRequestList);
        return this.postFluxWebClient(OkSpotCommons.BATCH_ORDERS,json,OrderVo.class);
    }

    @Override
    @SneakyThrows
    public Mono<OrderVo> cancelOrders(String id,String instrumentId) {
        final String path=UriComponentsBuilder.fromUriString(OkSpotCommons.CANCEL_ORDERS).build(id).getPath();
        Map map=new ConcurrentHashMap(2);
        map.put("instrument_id",instrumentId);
        ObjectMapper mapper = new ObjectMapper();
        final String json=mapper.writeValueAsString(map);
        return this.postWebClient(path,json,OrderVo.class);
    }

    @Override
    public Flux<OrderVo> batchCancelOrders(List<String> ids) {
        return null;
    }

    @Override
    public Mono<String> amendOrder(String instrumentId) {
        return null;
    }

    @Override
    public Flux<FillsVo> getAllFills(FillsRequest fillsRequest) {

        final String path=UriComponentsBuilder.fromUriString(OkSpotCommons.FILLS)
                .queryParam("instrumentId",fillsRequest.getInstrument_id())
                .queryParam("limit",fillsRequest.getLimit())
                .build().getPath();
        return this.getWebClient(path,FillsVo.class);
    }


}
